<template>
  <div class="main-card">
    <div class="topo-container">
      <!-- 头部的栏 -->
      <div class="header-warp">
        <el-tabs
          v-model="activeTab"
          type="card"
          closable
          @tab-click="changeCurrentTab"
          @tab-remove="removeTab"
        >
          <el-tab-pane
            v-for="item in editableTabs"
            :key="item.name"
            :label="item.title"
            :name="item.name"
          >
          </el-tab-pane>
        </el-tabs>
        <div class="project-manager">
          <el-tooltip
            class="item"
            effect="dark"
            content="创建项目"
            placement="top"
            :hide-after="1000"
          >
            <el-button
              icon="el-icon-plus"
              circle
              @click="openPopWindow(openType.new)"
              size="small"
              round
            ></el-button>
          </el-tooltip>
          <el-tooltip
            class="item"
            effect="dark"
            content="保存项目"
            placement="top"
            :hide-after="1000"
          >
            <el-button
              icon="el-icon-check"
              circle
              @click="saveProject"
              size="small"
              round
            ></el-button>
          </el-tooltip>
          <el-tooltip
            class="item"
            effect="dark"
            content="导入项目"
            placement="top"
            :hide-after="1000"
          >
            <el-button
              icon="el-icon-download"
              @click="openPopWindow(openType.import)"
              circle
              size="small"
              round
            ></el-button>
          </el-tooltip>
        </div>
      </div>

      <!-- 画布容器 -->
      <div id="mountNode"></div>

      <!-- 所有需要用弹窗的都在这里 -->
      <el-dialog title="提示" :visible.sync="dialogVisible" width="50%">
        <!-- 权重设置 code=1 -->
        <evaluate-table
          v-if="contentCode == openType.assess"
          :fid="activeNodeID"
          :trainingId="activeTab"
          :setScores="setScores"
        ></evaluate-table>
        <!-- 新建项目 code=0 -->
        <el-form
          v-else-if="contentCode == openType.new"
          ref="form"
          :model="project"
          label-width="80px"
        >
          <el-form-item label="项目名">
            <el-input v-model="project.name"></el-input>
          </el-form-item>
          <el-form-item label="项目介绍">
            <el-input
              type="textarea"
              :rows="5"
              placeholder="该项目是......"
              v-model="project.introduction"
            >
            </el-input>
          </el-form-item>
        </el-form>
        <!-- 新建节点，code=2 训练id（自动获取），节点类型，节点的label名称 -->
        <el-form
          v-else-if="contentCode == openType.createNode"
          :model="node"
          label-width="80px"
          style="width: 280px"
        >
          <!-- 节点名称 -->
          <el-form-item label="节点名称">
            <el-input v-model="node.label"></el-input>
          </el-form-item>
          <!-- 节点类型 -->
          <el-form-item label="节点类型">
            <el-select v-model="node.nodeType" placeholder="请选择节点类型">
              <el-option label="设备" :value="0"></el-option>
              <el-option label="属性" :value="1"></el-option>
            </el-select>
          </el-form-item>
          <!-- 设备类型 -->
          <el-form-item label="设备类型">
            <el-autocomplete
              v-model="node.device"
              :fetch-suggestions="querySearchAsync"
              @select="handleSelect"
            ></el-autocomplete>
          </el-form-item>
        </el-form>
        <!-- 导入项目 code=3 -->
        <el-table
          v-else-if="contentCode == 3"
          :data="projectData"
          style="width: 100%"
        >
          <el-table-column prop="name" label="训练名称"> </el-table-column>
          <el-table-column prop="createDate" label="创建时间">
          </el-table-column>
          <el-table-column prop="isOpen" label="是否启用">
            <template slot-scope="scope">
              <el-switch
                v-model="scope.row.isOpen"
                active-color="#13ce66"
                inactive-color="#dcdfe6"
              >
              </el-switch>
            </template>
          </el-table-column>
        </el-table>
        <!-- 通用的确定和取消，只是依据不同的code去做不同的事情 -->
        <span slot="footer" class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="confirm">确 定</el-button>
        </span>
      </el-dialog>
      <!-- 节点信息 -->
      <el-drawer
        title="属性面板"
        :visible.sync="nodeDetailShow"
        :before-close="handleClose"
        direction="rtl"
      >
        <!-- TODO:这里看情况是否可以分成三个部分：一个是基本属性的查看，如名称等，一个是 -->
        <el-table :data="nodeAttributesData" border style="width: 100%">
          <el-table-column prop="attrName" label="属性">
            <template slot-scope="scope">
              <table-cell
                :disable="true"
                v-model="scope.row.attrName"
              ></table-cell>
            </template>
          </el-table-column>
          <el-table-column prop="value" label="值|变量">
            <template slot-scope="scope">
              <table-cell v-model="scope.row.value"></table-cell>
            </template>
          </el-table-column>
        </el-table>
        <div class="footer-bar">
          <el-button @click="confirmAlterAttributions" type="primary"
            >确定</el-button
          >
        </div>
      </el-drawer>
    </div>
  </div>
</template>

<script>
import G6 from "@antv/g6";
import EvaluateTable from "@/components/EvaluateTable.vue";
import TableCell from "@/components/TableCell.vue";
import {
  deleteNode,
  getNodeDetail,
  insertScore,
  createNode,
} from "@/services/node";
import { open, create, save, getTrainings } from "@/services/training";
import { Message, MessageBox } from "element-ui";
// import { MessageBox } from 'element-ui';
// import markRequest from "@/utils/markRequest";
// import { BASE_URL } from "@/constant";
import { nanoid } from "nanoid";
// 定义数据
// let topoData = {};

export default {
  name: "TopologyDiagram",
  components: { EvaluateTable, TableCell },
  data() {
    return {
      dialogVisible: false,
      nodeDetailShow: false,
      nodeAttributesData: [
        {
          attrName: "测向",
          value: "",
        },
        {
          attrName: "定位",
          value: "",
        },
        {
          attrName: "名称",
          value: "third-movement",
        },
        {
          attrName: "公式",
          value: "",
        },
      ],
      editableTabs: [],
      activeTab: "",
      project: {},
      contentCode: 1, //0是创建项目，1是评估, 2是创建节点
      node: {},
      projectData: [
        {
          trainingId: "001",
          name: "可控核聚变",
          createDate: "2023-4-3 11:09:31",
          isOpen: false,
        },
        {
          trainingId: "002",
          name: "可控核聚变",
          createDate: "2023-4-3 11:09:31",
          isOpen: true,
        },
        {
          trainingId: "003",
          name: "可控核聚变",
          createDate: "2023-4-3 11:09:31",
          isOpen: false,
        },
      ],
      projectDataHasAlteration: [], //标识有改变的id
      activeNodeID: "",
      selectItem: "",
      scores: [],
      openType: {
        new: 0,
        assess: 1,
        import: 3,
        createNode: 2,
      },
      graphData: null,
    };
  },
  watch: {
    // graphData(newVal) {
    //   this.graph.read(newVal);
    // },
  },
  methods: {
    // 配置侧边栏确定的回调
    confirmAlterAttributions() {
      // 获取基本信息
      const training_id = "9a0c420812b8458883bc82dadc73238a";
      const node_id = "0d4ce6f40806478da65782bfbbb107d4";
      const attributes = this.nodeAttributesData;

      // 发送请求
      this.$http
        .post(`${this.$reqPrefix}/build/alterNodeDetail`, {
          training_id,
          node_id,
          attributes,
        })
        .then((res) => {
          if (res.data.code == 200) {
            Message.success("修改成功");
          }
        });
    },
    // 配置G6并渲染的方法
    setAndRenderTopoGraph() {
      // 实例化插件
      const grid = new G6.Grid();
      const minimap = new G6.Minimap();
      const menu = new G6.Menu({
        className: "context-menu-list",
        getContent(e) {
          const outDiv = document.createElement("ul");
          // outDiv.style.maxWidth = "180px";
          outDiv.style.listStyle = "none";
          outDiv.style.boxShadow = " rgba(0, 0, 0, 0.24) 0px 3px 8px";
          outDiv.style.backgroundColor = "#fff";
          outDiv.style.width = "100px";
          outDiv.style.borderRadius = "10px";
          outDiv.style.overflow = "hidden";
          outDiv.innerHTML = `
            <li class="context-item">新建</li>
            <li class="context-item">删除</li>
            <li class="context-item">查看</li>
            <li class="context-item">评估</li>
          `;
          return outDiv;
        },
        handleMenuClick: (target, item, graph) => {
          // console.log("target, item: ", target, item);
          // console.log()
          const context = target.innerHTML;
          // console.log('context: ', context);
          const contextMapFun = {
            // 新建的逻辑
            新建: () => {
              // 打开新建的弹窗
              // 弹窗内是表单，选择节点类型，填写节点名称，以及可能有一些其他的默认属性
              this.activeNodeID = item._cfg.id;

              this.openPopWindow(this.openType.createNode);
            },
            // 删除的逻辑
            删除: () => {
              // this.activeNode = item;
              // this.$http
              //   .get(`${this.$reqPrefix}/trainingBuild/deleteNode`, {
              //     params: {
              //       trainingId: this.activeTab,
              //       nodeId: item._cfg.id,
              //     },
              //   })
              deleteNode(this.activeTab, item._cfg.id).then((res) => {
                if (res.data.code == 200) {
                  // this.editableTabs = res.data.data;
                  // 暂时输出删除后的整个图
                  // console.log(res.data.data);
                  // this.changeRenderData(res.data.data);
                  window.graph.read(res.data.data);
                }
              });
            },
            // 修改的逻辑
            修改: () => {
              // console.log("修改");
              // this.activeNode = item;
              // this.nodeDetailShow = true;
              // // 拿到当前节点
              // const { attributes } = item._cfg.model || {};
              // this.nodeAttributesData.push(
              //   { attrName: "id", value: item._cfg.model.id },
              //   { attrName: "label", value: item._cfg.model.label }
              // );
              // for (const key in attributes) {
              //   that.nodeAttributesData.push({
              //     attrName: key,
              //     value: attributes[key],
              //   });
              // }
            },
            // 查看的逻辑
            查看: () => {
              // this.activeNode = item;
              this.activeNodeID = item._cfg.id;

              getNodeDetail(this.activeNodeID).then((res) => {
                if (res.data.code == 200) {
                  // console.log(res.data.data);
                  // this.nodeAttributesData = res.data.data
                  this.nodeAttributesData = res.data.data;
                  this.nodeDetailShow = true;
                } else {
                  alert(res.data.message);
                }
              });
            },
            // 评估的逻辑
            评估: () => {
              // this.activeNode = item;
              this.activeNodeID = item._cfg.id;
              this.openPopWindow(this.openType.assess);
            },
          };
          // 执行对应的逻辑
          contextMapFun[context]();
          // 数据更新结束后删除所有
          // graph.read(topoData);
        },
      });
      const toolbar = new G6.ToolBar();
      const tooltip = new G6.Tooltip({
        itemTypes: ["node"],
      });
      // 获取容器宽高
      const container = document.querySelector("#mountNode");
      const width = container.offsetWidth;
      const height = container.offsetHeight;

      const graph = new G6.Graph({
        container: "mountNode", // String | HTMLElement，必须，在 Step 1 中创建的容器 id 或容器本身
        width, //container.offsetWidth, // Number，必须，图的宽度
        height, // container.offsetHeight, // Number，必须，图的高度
        modes: {
          default: ["drag-canvas", "zoom-canvas", "drag-node"], // 允许拖拽画布、放缩画布、拖拽节点
        },
        // 全局配置节点的state样式
        nodeStateStyles: {
          // 鼠标 hover 上节点，即 hover 状态为 true 时的样式
          hover: {
            fill: "lightsteelblue",
          },
          // 鼠标点击节点，即 click 状态为 true 时的样式
          click: {
            stroke: "#5f95ff", //外圈|描边的颜色
            fill: "#f29a76", //内部填充的颜色
            lineWidth: 2,
          },
        },
        // 全局配置边的state样式
        edgeStateStyles: {
          // 鼠标点击边，即 click 状态为 true 时的样式
          click: {
            stroke: "steelblue",
          },
        },
        defaultNode: {
          type: "circle",
          labelCfg: {
            position: "bottom",
          },
        },
        defaultEdge: {
          style: {
            endArrow: true,
          },
        },
        layout: {
          type: "dagre",
          rankdir: "LR",
          preventOverlap: true,
          nodeSize: 30,
        },
        plugins: [grid, minimap, menu, toolbar, tooltip],
      });
      this.graph = graph;
      // 鼠标进入节点
      graph.on("node:mouseenter", (e) => {
        const nodeItem = e.item; // 获取鼠标进入的节点元素对象
        graph.setItemState(nodeItem, "hover", true); // 设置当前节点的 hover 状态为 true
      });

      // 鼠标离开节点
      graph.on("node:mouseleave", (e) => {
        const nodeItem = e.item; // 获取鼠标离开的节点元素对象
        graph.setItemState(nodeItem, "hover", false); // 设置当前节点的 hover 状态为 false
      });
      // window.graph = graph;
      // console.log("G6中的this",this)

      // 点击节点
      graph.on("node:click", (e) => {
        // 先将所有当前是 click 状态的节点置为非 click 状态
        // console.log(this)
        // this.dialogVisible = true;
        const clickNodes = graph.findAllByState("node", "click");
        clickNodes.forEach((cn) => {
          graph.setItemState(cn, "click", false);
        });
        const nodeItem = e.item; // 获取被点击的节点元素对象
        graph.setItemState(nodeItem, "click", true); // 设置当前节点的 click 状态为 true
      });

      // 点击边
      graph.on("edge:click", (e) => {
        // 先将所有当前是 click 状态的边置为非 click 状态
        const clickEdges = graph.findAllByState("edge", "click");
        clickEdges.forEach((ce) => {
          graph.setItemState(ce, "click", false);
        });
        const edgeItem = e.item; // 获取被点击的边元素对象
        graph.setItemState(edgeItem, "click", true); // 设置当前边的 click 状态为 true
      });
      // TODO: del topoData
      // graph.data(topoData); // 读取 Step 2 中的数据源到图上
      // graph.render(); // 渲染图
    },
    // 创建项目的回调
    /**
     * 逻辑：弹窗要求信息，然后发送请求，接收回参
     */
    handleClose(done) {
      // 拿到修改过后的属性属性重新设置里面的部分,id没法变，其他的都可以变
      // const nodeID = this.nodeAttributesData[0].value;
      // const label = this.nodeAttributesData[1].value;
      // // 拿到topoData对其中的那一项的属性进行更改
      // const attributes = {};
      // for (let index = 2; index < this.nodeAttributesData.length; index++) {
      //   let currentNode = this.nodeAttributesData[index];
      //   attributes[currentNode["attrName"]] = currentNode["value"];
      // }
      // for (const node of topoData.nodes) {
      //   if (node.id == nodeID) {
      //     // 进行更改
      //     if (node.label != label) node.label = label;
      //     node.attributes = attributes;
      //   }
      // }
      // this.nodeAttributesData = [];
      done();
    },
    // 改变选项卡的回调
    changeCurrentTab(tab, event) {
      // 请求
      // this.$http
      //   .get(
      //     `${this.$reqPrefix}/trainingBuild/openTraining?trainingId=${this.activeTab}`
      //   )
      // TODO:这里应该有问题this.activeTab不是最新的，应该有参数传递进来，然后更新
      const { name } = tab;
      this.activeTab = name;
      open(name).then((res) => {
        if (res.data.code == 200) {
          // TODO: 这里可能需要看着回的数据来写
          const { data } = res.data;
          // this.changeRenderData(data);
          // topoData.push(data)
          window.graph.read(data);
        }
      });
    },
    // 改变渲染数据的
    // changeRenderData(data) {
    //   topoData = data;
    //   window.graph.changeData(topoData, true);
    // },
    // 打开弹窗的回调
    openPopWindow(contentCode) {
      this.contentCode = contentCode;
      this.dialogVisible = true;
    },
    // 所有弹窗后确认的回调
    confirm() {
      const { contentCode } = this;
      switch (contentCode) {
        case this.openType.new: {
          // 创建项目的确认
          // TODO:实现uid的获取，感觉可以直接从上一个项目一个超链接跳转过来，然后全局把user给初始化了
          const uid = "1";
          const { name, introduction } = this.project;
          create(nanoid(), introduction, uid, name).then((res) => {
            if (res.data.code == 200) {
              const { name: title } = this.project;
              const { training_id: name } = res.data.data;
              this.editableTabs.push({
                title,
                name,
              });
              this.dialogVisible = false;
              // 这里是不是可以nodes和edges
              // TODO:我能不能自己写一个改变nodes和edges的数据，包括地址的改变，然后响应式的图就更新呢
              this.project = {};
            }
          });
          break;
        }
        case this.openType.assess: {
          if (this.scores.length == 0) {
            alert("数据不合法");
            return;
          }
          console.log("scores", this.scores);
          // 评估的确认
          // this.$http
          //   .post(`${this.$reqPrefix}/trainingBuild/insertScores`, {
          //     trainingId: this.activeTab,
          //     nodeId: this.activeNodeID,
          //     scores: this.scores,
          //   })
          insertScore(this.activeTab, this.activeNodeID, this.scores)
            .then((res) => {
              if (res.data.code == 200) {
                // 先暂时输出评分矩阵
                console.log("评分矩阵", res.data.data);
              } else {
                alert(res.data.message);
              }
              this.scores = [];
            })
            .finally(() => {
              this.dialogVisible = false;
            });
          // this.confirmMsg = "待确认";
          // 检查数据是否完整
          // 如果完整发送网路请求
          break;
        }
        case this.openType.createNode: {
          // 创建节点的确认
          // this.$http
          //   .post(`${this.$reqPrefix}/trainingBuild/createNode`, {
          //     trainingId: this.activeTab,
          //     nodeType: this.node.nodeType,
          //     fid: this.activeNodeID,
          //     deviceId: this.selectItem.id,
          //     label: this.node.label,
          //   })
          // TODO: 加一个中文
          createNode(
            this.node.label,
            this.activeTab,
            this.activeNodeID,
            "chinese"
          ).then((res) => {
            if (res.data.code == 200) {
              window.graph.read(res.data.data);
              this.node = {};
              this.dialogVisible = false;
            }
          });
        }
      }
    },
    // 保存项目的回调
    saveProject() {
      // 发送请求把所有数据上传一便
      // MessageBox.alert("保存有可能会导致数据丢失，是否继续？", "提示", {
      //   confirmButtonText: "确定",
      //   cancelButtonText: "取消",
      //   type: "warning",
      // })
      // .then(() => {
      //   Message.success("保存成功")
      // });

      // this.$http.post(`${this.$reqPrefix}/trainingBuild/save`, {
      //   trainingId: this.activeTab,
      // });
      MessageBox.confirm("保存有可能会导致数据丢失，是否继续？", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          save(this.activeTab).then(() => {
            Message.success("保存成功");
          });
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消保存",
          });
        });
    },
    // 点击选项卡的叉的回调
    /**
     * targetName:要删除的目标tab，为name属性，这里即id
     */
    removeTab(targetName) {
      // console.log('targetName: ', targetName);
      if (this.editableTabs.length == 1) {
        alert("至少要打开一个项目");
        return;
      }
      // 前台做的
      const uid = "1";
      const tabs = this.editableTabs;
      let activeName = this.activeTab;
      if (activeName === targetName) {
        tabs.forEach((tab, index) => {
          if (tab.name === targetName) {
            let nextTab = tabs[index + 1] || tabs[index - 1];
            if (nextTab) {
              activeName = nextTab.name;
            }
          }
        });
      }
      this.activeTab = activeName;
      this.editableTabs = tabs.filter((tab) => tab.name !== targetName);
      // 后台做的--把isOpen改为false
      // this.$http.get(
      //   `${this.$reqPrefix}/trainingBuild/deleteTraining?trainingId=${targetName}`
      // );
    },
    // 新建节点，给用户建议设备类型的回调
    querySearchAsync(queryString, cb) {
      // 获取所有推荐的内容
      /**
       * 请求参数：无
       * 返回参数：所有的设备类型
       */
      // TODO:这个没有
      this.$http
        .get(`${this.$reqPrefix}/trainingBuild/equipmentsName`)
        .then((res) => {
          if (res.data.code == 200) {
            const devicesType = res.data.data;
            const results = queryString
              ? devicesType.filter(this.createStateFilter(queryString))
              : devicesType;
            // 将结果传递出去
            cb(results);
          }
        });
    },
    // 新建节点依赖的函数，作为状态筛选器
    createStateFilter(queryString) {
      return (state) => {
        return (
          state.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
        );
      };
    },
    handleSelect(item) {
      this.selectItem = item;
    },
    setScores(scores) {
      this.scores = scores;
    },
  },
  mounted() {
    // 配置G6并渲染的方法
    this.setAndRenderTopoGraph();
    // 获取用户的所有项目
    const uid = "1";
    getTrainings()
      .then((res) => {
        if (res.data.code == 200) {
          const { trainings } = res.data.data;
          this.editableTabs = trainings.map((training) => ({
            name: training.training_id,
            title: training.chineseName,
          }));
          // this.activeTab = this.editableTabs[0].name;
          // this.graphData = res.data.data;
          return trainings[0];
        }
      })
      .then((firstTraining) => {
        // const { training_id } = firstTraining;
        const training_id = "001";
        this.activeTab = training_id;
        open(training_id).then((res) => {
          this.graphData = res.data.data;
        });
      });
  },
};
</script>

<style lang='less' scoped>
@itemHeight: 22px;
.main-card {
  height: calc(100% - 42px);
  padding: 20px;
  overflow: auto;
  border-radius: 5px;
  box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;

  .topo-container {
    height: 100%;
    position: relative;
    .header-warp {
      position: relative;
      .el-tabs {
        :deep(.el-tabs__header) {
          margin: 0;
        }
      }
      .project-manager {
        position: absolute;
        top: 0;
        right: 0;
      }
    }
    #mountNode {
      height: calc(100% - 40.8px);
      // overflow: hidden;
      :deep(.g6-component-toolbar) {
        left: 20px;
        top: 50px;
      }
      :deep(.g6-minimap) {
        position: absolute;
        top: 50px;
        right: 5px;
        overflow: hidden;
        border: 1px solid #ccc;
        border-radius: 5px;
      }
    }
    .el-dialog {
      // position: relative;
    }
  }
}

.footer-bar {
  width: 100%;
  position: absolute;
  bottom: 10px;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  .el-button {
    margin-right: 50px;
  }
}
</style>